using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Xml;
using Legacy.Core.SaveGameManagement;

namespace MMXSaveGameConverter
{
    internal static class Save2Xml
    {
        public static XmlDocument SerializeSaveGame(SaveGame sg)
        {
            if (sg == null)
                throw new ArgumentNullException("sg");

            XmlDocument doc = new XmlDocument();
            {
                XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", null, null);
                doc.AppendChild(dec);
            }

            XmlElement root = doc.CreateElement("SavedGame");
            doc.AppendChild(root);

            Serialize(sg, root);
            return doc;
        }

        private static void Serialize(SaveGame sg, XmlElement root)
        {
            var datas = sg.GetPrivateFieldValue<Dictionary<string, SaveGameData>>("m_saveGameData");

            SerializeMeta(sg.GetMeta(), root);
            foreach (SaveGameData data in datas.Values)
                Serialize(data, root);
        }

        private static void SerializeMeta(SaveGameMeta meta, XmlElement parent)
        {
            XmlElement node = CreateChildNode(parent, "SaveGameMeta");
            CreateAttribute(node, "Int64_TimeInTicks", meta.Time.Ticks);
            CreateAttribute(node, "Int64_PlayTimeInTicks", meta.PlayTime.Ticks);
            CreateAttribute(node, "Int32_SaveNumber", meta.SaveNumber);
            CreateAttribute(node, "Int32_SaveGameType", (int)meta.Type);
            CreateAttribute(node, "Int32_Difficulty", (int)meta.Difficulty);
        }

        private static void Serialize(SaveGameData data, XmlElement parent)
        {
            XmlElement node = CreateChildNode(parent, "SaveGameData");
            CreateAttribute(node, "id", data.ID);
            CreateAttribute(node, "sealed", data.GetPrivateFieldValue<bool>("m_sealed"));

            Serialize((data.GetPrivateFieldValue<Dictionary<string, bool>>("m_bools")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.Boolean, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, byte>>("m_bytes")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.Byte, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, short>>("m_shorts")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.Int16, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, ushort>>("m_ushorts")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.UInt16, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, int>>("m_ints")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.Int32, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, uint>>("m_uints")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.UInt32, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, long>>("m_longs")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.Int64, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, ulong>>("m_ulongs")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.UInt64, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, float>>("m_floats")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.Single, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, double>>("m_doubles")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.Double, node);
            Serialize((data.GetPrivateFieldValue<Dictionary<string, string>>("m_strings")).NullSafeSelect(p => Tuple.Create(p.Key, (object)p.Value)), XmlHelper.String, node);

            var objects = data.GetPrivateFieldValue<Dictionary<string, SaveGameData>>("m_dataObjects");
            if (objects != null && objects.Count > 0)
            {
                XmlElement childs = CreateChildNode(node, XmlHelper.Object);
                foreach (SaveGameData obj in objects.Values)
                    Serialize(obj, childs);
            }
        }

        private static void Serialize(IEnumerable<Tuple<string, object>> pairs, string name, XmlElement parent)
        {
            XmlElement node = null;

            IEnumerator<Tuple<string, object>> en = pairs.GetEnumerator();
            while (en.MoveNext())
            {
                if (node == null)
                    node = CreateChildNode(parent, name);

                XmlElement entry = CreateChildNode(node, "Entry");
                CreateAttribute(entry, "name", en.Current.Item1);
                CreateAttribute(entry, "value", en.Current.Item2);
            }
        }

        private static XmlElement CreateChildNode(XmlElement parent, string name)
        {
            XmlElement node = parent.OwnerDocument.CreateElement(name);
            parent.AppendChild(node);
            return node;
        }

        private static XmlAttribute CreateAttribute(XmlElement node, string name, object value)
        {
            XmlAttribute attr = node.OwnerDocument.CreateAttribute(name);
            attr.Value = Convert.ToString(value, CultureInfo.InvariantCulture);
            node.Attributes.Append(attr);
            return attr;
        }
    }
}